<script setup lang="ts">
import { onMounted, reactive, ref, unref } from 'vue'
import { useEventListener } from '@vueuse/core'
import type { WorkflowDesignRow } from '@/types/custom'
import { ElMessage } from 'element-plus'
import { clone } from 'xe-utils'

interface Props {
  contentRef: () => HTMLElement
  editRow: WorkflowDesignRow
}

const props = defineProps<Props>()
const emit = defineEmits(['on-paste'])

// 画矩形的起始点
let oldTop = 0
let oldLeft = 0
// 矩形容器
const moveRef = ref<HTMLDivElement>()
// 矩形宽高坐标样式
const moveStyle = reactive({
  top: '0',
  left: '0',
  width: '0',
  height: '0',
  bottom: '0',
  right: '0'
})
// 鼠标按下
const longPressedHook = ref(false)
// 鼠标按下时间
const longPressedHookTime = ref(Date.now())
// 按住Ctrl
const isControl = ref(false)
// 选中节点
const nodes = ref<WorkflowDesignNode[]>([])
// 选中线
const edges = ref<WorkflowDesignEdge[]>([])

/**
 * 重置矩形
 */
const onResetMove = () => {
  moveStyle.width = '0px'
  moveStyle.height = '0px'
  moveStyle.top = '0px'
  moveStyle.left = '0px'
}

/**
 * 获取选中
 */
const findSelected = () => {
  const instance = props.contentRef()
  const move = unref(moveRef)
  if (instance && move) {
    const isStartNode = props.editRow.nodes.some((f) => f.id === 'start')
    const isEndNode = props.editRow.nodes.some((f) => f.id === 'end')
    const data: string[] = []
    // 获取所有li元素
    const list: NodeListOf<HTMLLIElement> = instance.querySelectorAll('.workflow-right-clone')
    // 获取元素的边界F
    const moveRect = move.getBoundingClientRect()
    //判断每个块是否被遮罩盖住（即选中）
    list.forEach((element) => {
      // 获取元素的边界F
      const elementRect = element.getBoundingClientRect()
      if (
        elementRect.left < moveRect.right &&
        elementRect.right > moveRect.left &&
        elementRect.top < moveRect.bottom &&
        elementRect.bottom > moveRect.top
      ) {
        const id = element.dataset.id as string
        if (isStartNode || isEndNode) {
          if (id !== 'start' && id !== 'end') data.push(id)
        } else {
          data.push(id)
        }
      }
    })
    const ns = clone(
      props.editRow.nodes.filter((f) => data.includes(f.id)),
      true
    )
    const minTop = Math.min(...ns.map((v) => parseInt(v.top || '0'))) || 0
    const minLeft = Math.min(...ns.map((v) => parseInt(v.left || '0'))) || 0
    ns.forEach((v) => {
      const top = parseInt(v.top || '0') - minTop
      const left = parseInt(v.left || '0') - minLeft
      v.top = `${top < 0 ? 0 : top}px`
      v.left = `${left < 0 ? 0 : left}px`
    })
    nodes.value = ns
    edges.value = clone(
      props.editRow.edges.filter(
        (f) =>
          nodes.value.some((s) => s.id === f.sourceId) &&
          nodes.value.some((s) => s.id === f.targetId)
      ),
      true
    )
  }
  onResetMove()
}

const initEventListener = () => {
  const instance = props.contentRef()
  if (instance) {
    // 鼠标移动
    useEventListener(instance, 'mousemove', (event) => {
      if (!longPressedHook.value || !isControl.value) return
      if (event.pageX < oldLeft) {
        //向左拖
        moveStyle.left = event.pageX + 'px'
        moveStyle.width = oldLeft - event.pageX + 'px'
      } else {
        moveStyle.width = event.pageX - oldLeft + 'px'
      }
      if (event.pageY < oldTop) {
        //向上
        moveStyle.top = event.pageY + 'px'
        moveStyle.height = oldTop - event.pageY + 'px'
      } else {
        moveStyle.height = event.pageY - oldTop + 'px'
      }
      event.preventDefault()
    })

    // Ctrl+右键粘贴
    useEventListener(instance, 'contextmenu', (event) => {
      if (isControl.value) {
        event.preventDefault()
        if (nodes.value.length || edges.value.length) {
          const option = clone({ nodes: unref(nodes), edges: unref(edges) }, true)
          option.nodes.forEach((v) => {
            const top = parseInt(v.top || '0') + event.offsetY
            const left = parseInt(v.left || '0') + event.offsetX
            v.top = `${top < 0 ? 0 : top}px`
            v.left = `${left < 0 ? 0 : left}px`
          })
          emit('on-paste', option)
        }
      }
    })

    // 鼠标按下
    useEventListener(document.body, 'mousedown', (event) => {
      longPressedHookTime.value = Date.now()
      if (event.button === 2 || !isControl.value) return
      longPressedHook.value = true
      oldTop = event.pageY
      oldLeft = event.pageX
      moveStyle.top = event.pageY + 'px'
      moveStyle.left = event.pageX + 'px'
    })

    // 鼠标抬起
    useEventListener(document.body, 'mouseup', (event) => {
      if (event.button === 2) return
      longPressedHook.value = false
      const diff = Date.now() - longPressedHookTime.value
      if (diff <= 300) return onResetMove()
      // 获取元素的边界F
      const move = unref(moveRef)
      if (move) {
        const { top, height, width, left } = move.getBoundingClientRect()
        moveStyle.bottom = top + height + 'px'
        moveStyle.right = left + width + 'px'
      }
      findSelected()
    })

    useEventListener(document.body, 'keyup', (event) => {
      if (['ControlLeft', 'ControlRight'].includes(event.code)) {
        isControl.value = false
      }
    })
    useEventListener(document.body, 'keydown', (event) => {
      if (['ControlLeft', 'ControlRight'].includes(event.code)) {
        isControl.value = true
      }
    })
  }
}

const onCancel = () => {
  nodes.value.splice(0)
}

const onPaste = () => {
  ElMessage.warning('Shift+鼠标点击空白区域进行粘贴。')
}

onMounted(() => {
  initEventListener()
})
</script>

<template>
  <div v-if="nodes.length" class="div-move-nodes pa10">
    <div class="mb10 color-info">
      <span>已复制</span>
      <span class="color-primary ml5 mr5">{{ nodes.length }}</span>
      <span>个节点</span>
      <span class="color-warning ml5 mr5">{{ edges.length }}</span>
      <span>条线</span>
    </div>
    <div class="mb10">
      <el-button type="warning" @click="onPaste">Ctrl+右键粘贴</el-button>
      <el-button @click.prevent="onCancel">取消</el-button>
    </div>
    <div v-for="(node, index) in nodes" :key="index">
      {{ index + 1 }}. {{ node.label || node.id || node.nodeId }}
    </div>
  </div>
  <!-- 鼠标拖拽出的遮罩 （定位为  position:absolute）-->
  <!-- 遮罩最好是在绑定了mouseover事件的元素内部，并且不要阻止遮罩的冒泡事件。这样鼠标移到了遮罩上面，依然可以利用冒泡执行父元素的mouseover事件，就不会出现遮罩只能扩大，不能缩小的情况了（亲自试过） -->
  <div ref="moveRef" class="div-move" :style="moveStyle" v-if="longPressedHook"></div>
</template>

<style scoped>
.div-move {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 1000;
  border: var(--el-border-width) var(--el-border-style) var(--el-color-primary);
  pointer-events: none;
}

.div-move-nodes {
  position: fixed;
  right: 0;
  bottom: 0;
  z-index: 1000;
  background-color: var(--el-bg-color);
  box-shadow: var(--el-box-shadow);
}
</style>
